using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using FireStarter.CodeGeneration;
using FireStarterModeller.BO;
using FireStarterModeller;
using FireStarterModeller.Logic.ClassDefExport;
using FireStarterModeller.UI.Class;
using FireStarterModeller.UI.CodeGeneration;
using Habanero.Base;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.Faces.Base;
using Habanero.Faces.Win;
using DialogResult=Habanero.Faces.Base.DialogResult;
using DockStyle=Habanero.Faces.Base.DockStyle;


namespace FireStarterModeller.UI
{
    /// <summary>
    /// Provides a form used by a developer to generate code
    /// </summary>
    /// 
    public class CodeGenerationForm : FormWin
    {
        private readonly IButtonGroupControl _buttons;
        private readonly IControlFactory _controlFactory;
        private readonly DMSolution _dmSolution;
        private readonly IGroupBox _generationSettingsGroupBox;
        private readonly IGroupBox _projectsGroupBox;
        private ExtendedComboBoxMapper _controlMapper;


        public CodeGenerationForm(DMSolution dmSolution)
        {
            if (dmSolution == null) throw new ArgumentNullException("dmSolution");
            _dmSolution = dmSolution;
            if (_dmSolution.GenerationSettings == null)
            {
                _dmSolution.GenerationSettings = CreateDefaultGenerationSettings();
            }
            EnsureGenerationSettingsHasController(_dmSolution.GenerationSettings);
            _dmSolution.GenerationSettings.CodeGenerationController.LoadSolutionTypes();

            Text = "Generate Code";
            ControlBox = true;
            MaximizeBox = false;
            MinimizeBox = false;
            Size = new Size(600, 600);
            MinimumSize = new Size(400, 400);
            Padding = new Padding(10);

            Generator = new MasterGenerator();

            _controlFactory = GlobalUIRegistry.ControlFactory;
            _generationSettingsGroupBox = _controlFactory.CreateGroupBox();
            _projectsGroupBox = _controlFactory.CreateGroupBox();
            _buttons = _controlFactory.CreateButtonGroupControl();

            SetUpButtons();
            SetUpProjectsGroupBox();
            SetUpGenerationSettingsGroupBox();

            BorderLayoutManager borderLayoutManager = _controlFactory.CreateBorderLayoutManager(this);
            borderLayoutManager.AddControl(_generationSettingsGroupBox, BorderLayoutManager.Position.North);
            borderLayoutManager.AddControl(_projectsGroupBox, BorderLayoutManager.Position.Centre);
            borderLayoutManager.AddControl(_buttons, BorderLayoutManager.Position.South);
            this.Icon = FMResources.FirestarterICON;
        }

        public IGroupBox GenerationSettingsGroupBox
        {
            get { return _generationSettingsGroupBox; }
        }

        public IGroupBox ProjectsGroupBox
        {
            get { return _projectsGroupBox; }
        }

        public IButtonGroupControl Buttons
        {
            get { return _buttons; }
        }

        public IButton GenerateButton { get; private set; }

        public IButton CloseButton { get; private set; }

        public IButton AutoNamingButton { get; private set; }

        public DMSolution DMSolution
        {
            get { return _dmSolution; }
        }

        public IEditableGridControl ProjectTypeGenerationGridControl { get; private set; }

        public IPanelInfo GenerationSettingsPanelInfo { get; private set; }

        public IComboBox SolutionTypeComboBox
        {
            get { return (IComboBox) GenerationSettingsPanelInfo.FieldInfos["SolutionTypeID"].InputControl; }
        }

        public BusinessObjectCollection<ProjectTypeGeneration> ProjectTypes
        {
            get
            {
                return
                    (BusinessObjectCollection<ProjectTypeGeneration>)
                    ProjectTypeGenerationGridControl.BusinessObjectCollection;
            }
            set { ProjectTypeGenerationGridControl.BusinessObjectCollection = value; }
        }

        public IMasterGenerator Generator { get; set; }

        public DirectorySelectorControl SolutionDirectoryControl
        {
            get
            {
                return
                    (DirectorySelectorControl) GenerationSettingsPanelInfo.FieldInfos["SolutionDirectory"].InputControl;
            }
        }

        public DirectorySelectorControl HabaneroDllPathControl
        {
            get { return (DirectorySelectorControl) GenerationSettingsPanelInfo.FieldInfos["HabaneroDllPath"].InputControl; }
        }

        private static void EnsureGenerationSettingsHasController(GenerationSettings generationSettings)
        {
            if (generationSettings.CodeGenerationController == null)
            {
                generationSettings.CodeGenerationController = new CodeGenerationController(generationSettings);
            }
        }

        /// <summary>
        /// Creates the default project types and their associated solution
        /// </summary>
        private GenerationSettings CreateDefaultGenerationSettings()
        {
            var generationSettings = new GenerationSettings();
            generationSettings.CodeGenerationController = new CodeGenerationController(generationSettings);
            generationSettings.Solution = _dmSolution;
            if (Path.IsPathRooted(DMSolution.SolutionDirectory))
            {
                generationSettings.SolutionDirectory = "source";
            }
            return generationSettings;
        }

        private void SetUpGenerationSettingsGroupBox()
        {
            GenerationSettingsGroupBox.Text = "Solution Settings";

            ClassDef classDef = ClassDef.Get<GenerationSettings>();
            var panelBuilder = new PanelBuilder(_controlFactory);
            GenerationSettingsPanelInfo = panelBuilder.BuildPanelForForm(classDef.GetUIDef("default").UIForm);
            
            GenerationSettingsPanelInfo.BusinessObject = _dmSolution.GenerationSettings;
            IPanel panel = GenerationSettingsPanelInfo.Panel;
            panel.Dock = DockStyle.Fill;
            GenerationSettingsGroupBox.Controls.Add(panel);
            GenerationSettingsGroupBox.Height = GenerationSettingsPanelInfo.MinimumPanelHeight + 20;
            _controlMapper =
                (ExtendedComboBoxMapper)GenerationSettingsPanelInfo.FieldInfos["DatabaseConnectionID"].ControlMapper;
            _controlMapper.ShowPopupForm();
        }

        private void SetUpProjectsGroupBox()
        {
            ProjectsGroupBox.Text = "Projects";

            AutoNamingButton = _controlFactory.CreateButton();
            AutoNamingButton.Width = 200;
            AutoNamingButton.Text = "Auto Create Project Naming";

            AutoNamingButton.Click += AutoNamingButton_Click;

            ProjectTypeGenerationGridControl = _controlFactory.CreateEditableGridControl();
            ProjectTypeGenerationGridControl.Initialise(ClassDef.Get<ProjectTypeGeneration>());
            ProjectTypeGenerationGridControl.BusinessObjectCollection =
                _dmSolution.GenerationSettings.ProjectTypeGenerations;

            ProjectTypeGenerationGridControl.AllowUsersToAddBO = false;
            ProjectTypeGenerationGridControl.AllowUsersToDeleteBO = false;
            ProjectTypeGenerationGridControl.Buttons.Visible = false;
            ProjectTypeGenerationGridControl.Grid.AllowUserToAddRows = false;

            BorderLayoutManager borderLayoutManager = _controlFactory.CreateBorderLayoutManager(ProjectsGroupBox);
            borderLayoutManager.GapSize = 1;
            borderLayoutManager.AddControl(AutoNamingButton, BorderLayoutManager.Position.North);
            borderLayoutManager.AddControl(ProjectTypeGenerationGridControl, BorderLayoutManager.Position.Centre);

            SetupCustomEditableGridBehaviour();
        }

        private void SetupCustomEditableGridBehaviour()
        {
            var editableGrid = (EditableGridWin) ProjectTypeGenerationGridControl.Grid;
            editableGrid.CellValueChanged += EditableGrid_CellValueChanged;

            // Workaround for problem where it goes into edit mode and doesn't come out
            // See: http://www.chillisoft.co.za/blog/?p=46
            foreach (IDataGridViewColumn column in editableGrid.Columns)
            {
                if (column.Control is DataGridViewCheckBoxColumn)
                    {
                        var checkBoxColumn = (DataGridViewCheckBoxColumn) column.Control;
                        checkBoxColumn.ReadOnly = true;
                    }
            }

            editableGrid.CellClick += delegate(object sender, DataGridViewCellEventArgs e)
                  {
                      if (editableGrid.Columns[e.ColumnIndex].Control is
                          DataGridViewCheckBoxColumn)
                      {
                          IDataGridViewCell affectedCell =
                              editableGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];
                          bool currentValue = Convert.ToBoolean(affectedCell.Value);
                          affectedCell.Value = !currentValue;
                      }
                  };
        }

        private void EditableGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            DisplayAlphaWarningIfActivatingTestProject(e);
        }

        /// <summary>
        /// Once the test generation has become stable we can remove this message
        /// </summary>
        private void DisplayAlphaWarningIfActivatingTestProject(DataGridViewCellEventArgs e)
        {
            var editableGrid = (EditableGridWin) ProjectTypeGenerationGridControl.Grid;
            if (editableGrid.Columns[e.ColumnIndex].Control is DataGridViewCheckBoxColumn)
            {
                object checkBoxValue = editableGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
                var projectTypeGeneration = (ProjectTypeGeneration) editableGrid.GetBusinessObjectAtRow(e.RowIndex);
                if (projectTypeGeneration.ProjectType.Name.ToUpper().Contains("TEST.BO") && (bool) checkBoxValue)
                    //!projectTypeGeneration.ToBeGenerated.Value)
                {
                    _controlFactory.ShowMessageBox(
                        "Test Generation for Business Objects is an alpha feature.  Some class structures may cause compilation or test failures.");
                }
            }
        }

        private void AutoNamingButton_Click(object sender, EventArgs e)
        {
            GenerationSettings generationSettings = _dmSolution.GenerationSettings;
            foreach (ProjectTypeGeneration projectTypeGeneration in generationSettings.ProjectTypeGenerations)
            {
                generationSettings.CodeGenerationController.AutoGenerateProjectName(projectTypeGeneration);
            }
        }

        private void SetUpButtons()
        {
            CloseButton = _buttons.AddButton("Close");
            GenerateButton = _buttons.AddButton("Generate");
            CancelButton = (IButtonControl) CloseButton;
            CloseButton.Click += delegate { Close(); };
            GenerateButton.Click += GenerateButtonClicked;
        }

        private void GenerateButtonClicked(object sender, EventArgs e)
        {
            if (!CanGenerate()) return;

            try
            {
                Generator.ClassDefsXml = XmlWriterClassDMs.CreateClassDMXml(DMSolution);
                Result result;
                try
                {
                    result = Generate();
                }
                finally
                {
                    Cursor = Cursors.Default;
                }
                _controlFactory.ShowMessageBox(!result.Successful ? result.Message : "Code successfully generated.");
            }
            catch (Exception ex)
            {
                GlobalRegistry.UIExceptionNotifier.Notify(ex, "", "Error ");
            }
        }

        private Result Generate()
        {
            Result result;
            Cursor = Cursors.WaitCursor;
            Generator.SetupGenerator(DMSolution);
//                        CleanUpSolution();
            DMSolution.GenerationSettings.Save();
            result = Generator.Generate();
            CleanUpSolution();
            return result;
        }

        private bool CanGenerate()
        {
            Cursor.Current = Cursors.Default;
            IList<IBOError> boErrors = _dmSolution.Validate();
            if (boErrors.Count == 0) return true;
            bool hasWarningsOnly = SolutionValidator.HasWarningsOnly(boErrors);
            ShowErrors showErrors = new ShowErrors(_dmSolution,GlobalUIRegistry.ControlFactory);
            return (showErrors.ShowDialog() == (System.Windows.Forms.DialogResult) DialogResult.OK && hasWarningsOnly);
        }
        //Cleans up all the cloned classes.
        private void CleanUpSolution()
        {
            var classes = new BusinessObjectCollection<DMClass>();
            classes.LoadAll();
            ITransactionCommitter transactionCommitter = BORegistry.DataAccessor.CreateTransactionCommitter();
            foreach (DMClass dmClass in classes.ToArray())
            {
                if (dmClass.AssemblyID == null)//These are cloned classes
                {
                    dmClass.MarkForDelete();
                    transactionCommitter.AddBusinessObject(dmClass);
                }
                else
                {
                    dmClass.ClearClonedClass();//Clears the reference to the Cloned Class from the origional class.
                }
            }
            transactionCommitter.CommitTransaction();
        }
    }
}